package org.xiaoyu.rpc.seralization.support;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.util.Pool;
import de.javakaffee.kryoserializers.*;
import lombok.extern.slf4j.Slf4j;
import org.xiaoyu.rpc.common.exception.SerializeException;
import org.xiaoyu.rpc.seralization.Serializer;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.InvocationHandler;
import java.util.*;

/**
 * Created By Have
 * 2021/7/5 15:15
 */
@Slf4j
public class KryoSerializer implements Serializer {

    @Override
    public int getSerializerAlgorithm() {
        return SerializerAlgorithm.KRYO.getType();
    }

    @Override
    public byte[] serializer(Object obj) {
        Kryo kryo = KryoPool.POOL.obtain();
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()){
            Output output = new Output(byteArrayOutputStream);
            kryo.writeObject(output, obj);
            return output.toBytes();
        } catch (Exception e) {
            log.info("kryo序列化错误:[{}]", e.getMessage(), e);
            throw new SerializeException("Kryo序列化错误");
        } finally {
            KryoPool.POOL.free(kryo);
        }
    }

    @Override
    public <T> T deserializer(Class<T> clazz, byte[] bytes) {
        Kryo kryo = KryoPool.POOL.obtain();
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes)) {
            Input input = new Input(byteArrayInputStream);
            T object = kryo.readObject(input, clazz);
            return object;
        } catch (Exception e) {
            System.out.println(e.getMessage());
            log.info("kryo反序列化错误:{}", e.getMessage(), e);
            throw new SerializeException("kryo反序列化错误");
        } finally {
            KryoPool.POOL.free(kryo);
        }

    }

    public static class KryoPool{
        private static final Pool<Kryo> POOL;

        static {
            POOL = new Pool<Kryo>(true, false, 300) {
                @Override
                protected Kryo create() {
                    Kryo kryo = new Kryo();
                    kryo.setReferences(true);
                    kryo.setRegistrationRequired(false);
                    kryo.register( Arrays.asList( "" ).getClass(), new ArraysAsListSerializer() );
                    kryo.register( Collections.EMPTY_LIST.getClass(), new CollectionsEmptyListSerializer() );
                    kryo.register( Collections.EMPTY_MAP.getClass(), new CollectionsEmptyMapSerializer() );
                    kryo.register( Collections.EMPTY_SET.getClass(), new CollectionsEmptySetSerializer() );
                    kryo.register( Collections.singletonList( "" ).getClass(), new CollectionsSingletonListSerializer() );
                    kryo.register( Collections.singleton( "" ).getClass(), new CollectionsSingletonSetSerializer() );
                    kryo.register( Collections.singletonMap( "", "" ).getClass(), new CollectionsSingletonMapSerializer() );
                    kryo.register( GregorianCalendar.class, new GregorianCalendarSerializer() );
                    kryo.register( InvocationHandler.class, new JdkProxySerializer() );
                    UnmodifiableCollectionsSerializer.registerSerializers( kryo );
                    SynchronizedCollectionsSerializer.registerSerializers( kryo );
                    return kryo;
                }
            };
        }
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            Kryo obtain = KryoPool.POOL.obtain();
            KryoPool.POOL.free(obtain);

        }
        System.out.println("Time Consuming:" + (System.currentTimeMillis() - start));
    }
}
